/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package ships.core.models;

import com.jogamp.opencl.CLBuffer;
import com.jogamp.opencl.CLCommandQueue;
import com.jogamp.opencl.CLContext;
import com.jogamp.opencl.CLDevice;
import com.jogamp.opencl.CLMemory;
import com.jogamp.opencl.CLPlatform;
import java.nio.Buffer;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.logging.Level;
import java.util.logging.Logger;
import ships.core.exceptions.InvalidValueException;

/**
 *
 * @author fqhuy
 */
public class PinnedHostMemoryModel extends HostMemoryModel {

    protected CLContext context;
    protected CLDevice[] devices;
    //protected ArrayList<CLCommandQueue> queues;
    protected CLCommandQueue queue = null;
    protected ByteBuffer h_data = null;
    CLBuffer<?> cmPinnedData = null;

    public PinnedHostMemoryModel() {
        CLPlatform[] platforms = CLPlatform.listCLPlatforms();
        CLPlatform platform = platforms[0];
        CLDevice device = platform.getMaxFlopsDevice();
        devices = new CLDevice[1];
        devices[0] = device;
        context = CLContext.create(device);
    }
    @Override
    public <E extends Number> Buffer allocate(int size, Class<E> cls) {
        if (cls == Byte.class) {
            cmPinnedData = context.createByteBuffer(size, CLMemory.Mem.READ_WRITE, CLMemory.Mem.ALLOCATE_BUFFER);
        } else if (cls == Short.class) {
            cmPinnedData = context.createShortBuffer(size, CLMemory.Mem.READ_WRITE, CLMemory.Mem.ALLOCATE_BUFFER);
        } else if (cls == Integer.class) {
            cmPinnedData = context.createIntBuffer(size, CLMemory.Mem.READ_WRITE, CLMemory.Mem.ALLOCATE_BUFFER);
        } else if (cls == Long.class) {
            cmPinnedData = context.createLongBuffer(size, CLMemory.Mem.READ_WRITE, CLMemory.Mem.ALLOCATE_BUFFER);
        } else if (cls == Float.class) {
            cmPinnedData = context.createFloatBuffer(size, CLMemory.Mem.READ_WRITE, CLMemory.Mem.ALLOCATE_BUFFER);
        } else if (cls == Double.class) {
            cmPinnedData = context.createDoubleBuffer(size, CLMemory.Mem.READ_WRITE, CLMemory.Mem.ALLOCATE_BUFFER);
        } else {
            cmPinnedData = context.createByteBuffer(size, CLMemory.Mem.READ_WRITE, CLMemory.Mem.ALLOCATE_BUFFER);
            // throw new InvalidValueException();
        }
                
        queue = devices[0].createCommandQueue();

        h_data = queue.putMapBuffer(cmPinnedData, CLMemory.Map.WRITE, true);
        
        Buffer b = null;

        if (cls == Short.class) {
            b = h_data.asShortBuffer();
        } else if (cls == Integer.class) {
            b = h_data.asIntBuffer();
        } else if (cls == Long.class) {
            b = h_data.asLongBuffer();
        } else if (cls == Float.class) {
            b = h_data.asFloatBuffer();
        } else if (cls == Double.class) {
            b = h_data.asDoubleBuffer();
        } else {
            b = h_data;
        }

        //cmPinnedData = cmPinnedData.cloneWith(h_data);

        //queue.putUnmapMemory(cmPinnedData);

        //queue.release();

        return b;
        //throw new UnsupportedOperationException("Not supported yet.");
    }

    public void unmap() {
        cmPinnedData = cmPinnedData.cloneWith(h_data);
        queue.putUnmapMemory(cmPinnedData);
    }

    @Override
    protected void finalize() throws Throwable {
        super.finalize();
        unmap();
        cmPinnedData.release();
        queue.release();
        context.release();

    }
}
